package com.cloud.tsp.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.api.annotation.LocalLock;
import com.cloud.api.domain.forward.ResponseData;
import com.cloud.api.domain.job.QueryService;
import com.cloud.api.domain.service.*;
import com.cloud.api.myenum.CompanyEnum;
import com.cloud.api.myenum.StationType;
import com.cloud.api.utils.ExcelUtils;
import com.cloud.core.exception.MyException;
import com.cloud.core.util.R;
import com.cloud.mybatis.config.LoginHolder;
import com.cloud.mybatis.domain.TableDataInfo;
import com.cloud.tsp.config.DateUtils;
import com.cloud.tsp.config.SmsCodeVerify;
import com.cloud.tsp.mapper.StationAuthorizationDownloadHistoryMapper;
import com.cloud.tsp.mapper.StationServiceBaseInfoMapper;
import com.cloud.tsp.service.CertificateService;
import com.cloud.tsp.service.IStationAuditRecordService;
import com.cloud.tsp.service.IStationServiceBaseInfoService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务站授权信息表 服务实现类
 * </p>
 *
 * @author haohaibo
 * @since 2025-03-12
 */
@Service("stationServiceBaseInfoService")
public class StationServiceBaseInfoServiceImpl extends ServiceImpl<StationServiceBaseInfoMapper, StationServiceBaseInfo> implements IStationServiceBaseInfoService {

    private static final String TOPIC_NAME = "SERVICE_STATION";
    private static final String BOOTSTRAP_SERVERS = "116.147.38.18:9094,116.147.38.18:9095,116.147.38.18:9096";

    @Resource
    private StationServiceBaseInfoMapper stationServiceBaseInfoMapper;
    @Resource
    private IStationAuditRecordService stationAuditRecordService;
    @Resource(name = "myRedis")
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private StationAuthorizationDownloadHistoryMapper stationAuthorizationDownloadHistoryMapper;

    @Resource
    private CertificateService certificateService;

    @Resource
    private SmsCodeVerify smsCodeVerify;

    @Override
    public TableDataInfo<StationServiceBaseInfo> selectServiceStationList(QueryService para) {
        Page<StationServiceBaseInfo> page = new Page<>(para.getPage(), para.getPageSize());
        IPage<StationServiceBaseInfo> data = stationServiceBaseInfoMapper.selectServiceStationList(page, para);
        return TableDataInfo.build(data);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public String InsertOrUpdateStationInfo(StationInfo para) {
        LambdaQueryWrapper<StationServiceBaseInfo> queryName = new LambdaQueryWrapper<>();
        queryName.eq(StationServiceBaseInfo::getName, para.getName());
        StationServiceBaseInfo serviceBase= stationServiceBaseInfoMapper.selectOne(queryName,false);
        if (serviceBase != null && !serviceBase.getId().equals(para.getId())){
            throw new MyException("wds.exception.stationName");
        }

        LambdaQueryWrapper<StationServiceBaseInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StationServiceBaseInfo::getContactPhone, para.getContactPhone());
        StationServiceBaseInfo serviceBaseInfo = stationServiceBaseInfoMapper.selectOne(queryWrapper,false);
        if (serviceBaseInfo != null && !serviceBaseInfo.getId().equals(para.getId())){
            throw new MyException("wds.exception.stationPhone");
        }
        StationServiceBaseInfo info = new StationServiceBaseInfo();
        info.setSubjectName(para.getName());
        info.setSalesCompany(CompanyEnum.getDesc1(para.getSalesCompany().getValue()));
        BeanUtils.copyProperties(para, info);

        StationInfoCopy stationInfoCopy = new StationInfoCopy();
        BeanUtils.copyProperties(para, stationInfoCopy);
        stationInfoCopy.setSalesCompany(info.getSalesCompany().getDesc());
        this.saveOrUpdate(info);
        stationInfoCopy.setId(info.getId());
        sendKafka(stationInfoCopy);
        return info.getId();
    }

    public void sendKafka(StationInfoCopy para){
        // 配置 Kafka 生产者
        Properties props = new Properties();
        props.put("bootstrap.servers", BOOTSTRAP_SERVERS);
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("enable.idempotence", "true");    // 开启幂等性

        try (Producer<String, String> producer = new KafkaProducer<>(props)) {
            producer.send(new ProducerRecord<>(TOPIC_NAME, "", JSONUtil.toJsonStr(para)));
            System.out.println("Message sent successfully");
        }catch (Exception e){
            throw new MyException("wds.exception.kafkaSend");
        }
    }

    @Override
    public String InsertOrUpdateStationAuth(StationAuth para) {
        StationServiceBaseInfo byId = stationServiceBaseInfoMapper.selectById(para.getId());
        if (byId == null) {
            throw new MyException("wds.exception.dataNotExist");
        }
        StationServiceBaseInfo info = new StationServiceBaseInfo();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        info.setAuthorizationStart(para.getAuthorizationStart().format(formatter));
        info.setAuthorizationEnd(para.getAuthorizationEnd().format(formatter));
        BeanUtils.copyProperties(para, info);
        info.setSignDate(para.getSignDate().format(formatter));
        this.saveOrUpdate(info);
        return info.getId();
    }

    @Override
    @LocalLock(key = "import",value = "#{key}", expire = 10000)
    public ResponseData updateServiceStationAuth(MultipartFile file, LocalDate authorizationStart, LocalDate authorizationEnd, String key) throws IOException {
        if (authorizationEnd.isBefore(authorizationStart)){
            throw new MyException("授权结束时间不能早于授权开始时间");
        }
        List<String> headers = readExcelHeaders(file);
        List<String> header = ExcelUtils.getExcelImportFieldsName(ServiceExcel.class);
        if (!header.equals(headers)) {
            throw new MyException("wds.exception.importHeaderError");
        }
        List<ServiceExcel> list = ExcelUtils.read(file, ServiceExcel.class);
        if (list.isEmpty()){
            return null;
        }
        if (list.size() > 500){
            throw new MyException("wds.exception.importData");
        }
        List<ServiceExcel> failList = new ArrayList<>();
        List<String> ids = checkData(list, failList);
        //批量通过id修改数据
        if (!ids.isEmpty()) {
            LambdaUpdateWrapper<StationServiceBaseInfo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.in(StationServiceBaseInfo::getId, ids)
                    .set(StationServiceBaseInfo::getAuthorizationStart, authorizationStart)
                    .set(StationServiceBaseInfo::getAuthorizationEnd, authorizationEnd)
                    .set(StationServiceBaseInfo::getUpdateUser, LoginHolder.getLoginUser().getUid())
                    .set(StationServiceBaseInfo::getUpdateTime, LocalDateTime.now())
                    .setSql("authorization_address = NULL");
            stationServiceBaseInfoMapper.update(updateWrapper);
        }
        String name = null;
        if (!failList.isEmpty()) {
            name = exportErrList(failList);
        }
        return new ResponseData().setFileName(name).setErrCount(failList.size());
    }

    @Override
    public ServiceStatistics getServiceStationStatistics() {
        ServiceStatistics statistics = new ServiceStatistics();
        long count = stationAuditRecordService.count(new LambdaQueryWrapper<StationAuditRecord>().eq(StationAuditRecord::getAuditStatus, 0));
        List<StationServiceBaseInfo> list = stationServiceBaseInfoMapper.selectList(null);
        Map<StationType, Long> collect = list.stream()
                .filter(a -> a.getCategory() != null)
                .collect(Collectors.groupingBy(StationServiceBaseInfo::getCategory, Collectors.counting()));

        long nullCount = list.stream().filter(stationServiceBaseInfo -> stationServiceBaseInfo.getAuthorizationStart() == null).count();
        statistics.setServiceCount(collect.get(StationType.REPAIR_STATION) == null ? 0 : collect.get(StationType.REPAIR_STATION));
        statistics.setDealerCount(collect.get(StationType.DEALER) == null ? 0:collect.get(StationType.DEALER));
        statistics.setUnauthorizedCount(nullCount);
        statistics.setApplyCount(count);
        return statistics;
    }

    @Override
    public void downloadTemplate(HttpServletResponse response) throws IOException {
        String fileName = "table.downloadTemplate.station";
        ExcelUtils.export(response, fileName, Collections.emptyList(), ServiceTempExcel.class);
    }

    @Override
    public void exportData(HttpServletResponse response, QueryService para) throws IOException {
        List<ServiceExportExcel> list = stationServiceBaseInfoMapper.exportServiceStationList(para);
        ExcelUtils.export(response, "table.download.station", list, ServiceExportExcel.class);
    }

    @Override
    public R<String> downloadAuthorization(QueryAuthorization para) {

        String code = para.getCode();
        String phone = para.getContactPhone();
        LambdaQueryWrapper<StationServiceBaseInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StationServiceBaseInfo::getContactPhone, phone);

        StationServiceBaseInfo stationServiceBaseInfo = stationServiceBaseInfoMapper.selectOne(queryWrapper);
        if(stationServiceBaseInfo == null){
            throw new MyException("wds.exception.phoneNotFound");
        }

        // 验证验证码
        smsCodeVerify.verifySmsCode(phone, code);

        CertificateRequest request = new CertificateRequest();

        String subjectName = stationServiceBaseInfo.getSubjectName();
        if (!StringUtils.hasText(subjectName)) {
            throw new MyException("证书主体名称没有赋值");
        }
        request.setSubjectName(subjectName);

        String subjectRegion = stationServiceBaseInfo.getSubjectRegion();
        if (!StringUtils.hasText(subjectRegion)) {
            throw new MyException("证书主体地区没有赋值");
        }
        request.setSubjectRegion(subjectRegion);

        String authorizedModels = stationServiceBaseInfo.getAuthorizedModels();
        if (!StringUtils.hasText(authorizedModels)) {
            throw new MyException("证书授权车型没有赋值");
        }
        request.setAuthorizedModels(authorizedModels);

        String authorizationStart = stationServiceBaseInfo.getAuthorizationStart();
        if (StringUtils.hasText(authorizationStart)) {
            String formattedStartDate = DateUtils.formatDateString(authorizationStart, "yyyy-MM-dd", "yyyy年MM月dd日");
            request.setAuthorizationStart(formattedStartDate);
        } else {
            throw new MyException("证书授权开始日期没有赋值");
        }

        String authorizationEnd = stationServiceBaseInfo.getAuthorizationEnd();
        if (StringUtils.hasText(authorizationEnd)) {
            String formattedEndDate = DateUtils.formatDateString(authorizationEnd, "yyyy-MM-dd", "yyyy年MM月dd日");
            request.setAuthorizationEnd(formattedEndDate);
        } else {
            throw new MyException("证书授权结束日期没有赋值");
        }

        CompanyEnum salesCompany = stationServiceBaseInfo.getSalesCompany();
        if (salesCompany != null) {
            request.setAuthorizationCompany(salesCompany.getDesc());
        } else {
            throw new MyException("证书授权企业没有赋值");
        }

        String signDate = stationServiceBaseInfo.getSignDate();

        if (StringUtils.hasText(signDate)) {
            request.setSignDate(DateUtils.toChineseDate(signDate));
        } else {
            throw new MyException("证书签约日期没有赋值");
        }

//        request.setCurrentDate(DateUtils.toChineseDate(DateUtils.formatDateToString(LocalDate.now())));


        byte[] pdf = new byte[0];
        try {
            pdf = certificateService.generatePdf(request);
        } catch (IOException e) {
            return R.failed("生成 PDF 失败");
        }
        if (pdf == null || pdf.length == 0) {
            return R.failed("生成 PDF 失败");
        }

        // 将 PDF 数据转换为 Base64 编码
        String base64Pdf = Base64.getEncoder().encodeToString(pdf);
        StationAuthorizationDownloadHistory downloadHistory = new StationAuthorizationDownloadHistory();
        downloadHistory.setContactPhone(phone);
        downloadHistory.setServiceStationId(stationServiceBaseInfo.getId());
        stationAuthorizationDownloadHistoryMapper.insert(downloadHistory);
        return R.ok(base64Pdf);
    }

    @Override
    public R<String> generateCertInfos(CertificateInfos para) {

        StationServiceBaseInfo serviceBaseInfo = stationServiceBaseInfoMapper.selectById(para.getServiceStationId());
        if(serviceBaseInfo == null){
            throw new MyException("wds.exception.stationNotFound");
        }
        CertificateRequest request = new CertificateRequest();
        String subjectName = serviceBaseInfo.getSubjectName();
        if (!StringUtils.hasText(subjectName)) {
            throw new MyException("证书主体名称没有赋值");
        }
        request.setSubjectName(subjectName);

        String subjectRegion = serviceBaseInfo.getSubjectRegion();
        if (!StringUtils.hasText(subjectRegion)) {
            throw new MyException("证书主体地区没有赋值");
        }
        request.setSubjectRegion(subjectRegion);

        String authorizedModels = serviceBaseInfo.getAuthorizedModels();
        if (!StringUtils.hasText(authorizedModels)) {
            throw new MyException("证书授权车型没有赋值");
        }
        request.setAuthorizedModels(authorizedModels);

        String authorizationStart = serviceBaseInfo.getAuthorizationStart();
        if (StringUtils.hasText(authorizationStart)) {
            String formattedStartDate = DateUtils.formatDateString(authorizationStart, "yyyy-MM-dd", "yyyy年MM月dd日");
            request.setAuthorizationStart(formattedStartDate);
        } else {
            throw new MyException("证书授权开始日期没有赋值");
        }

        String authorizationEnd = serviceBaseInfo.getAuthorizationEnd();
        if (StringUtils.hasText(authorizationEnd)) {
            String formattedEndDate = DateUtils.formatDateString(authorizationEnd, "yyyy-MM-dd", "yyyy年MM月dd日");
            request.setAuthorizationEnd(formattedEndDate);
        } else {
            throw new MyException("证书授权结束日期没有赋值");
        }

        CompanyEnum salesCompany = serviceBaseInfo.getSalesCompany();
        if (salesCompany != null) {
            request.setAuthorizationCompany(salesCompany.getDesc());
        } else {
            throw new MyException("证书授权企业没有赋值");
        }

        String signDate = serviceBaseInfo.getSignDate();

        if (StringUtils.hasText(signDate)) {
            request.setSignDate(DateUtils.toChineseDate(signDate));
        } else {
            throw new MyException("证书签约日期没有赋值");
        }

//        request.setCurrentDate(DateUtils.toChineseDate(DateUtils.formatDateToString(LocalDate.now())));

        byte[] pdf = new byte[0];
        try {
            pdf = certificateService.generatePdf(request);
        } catch (IOException e) {
            return R.failed("生成 PDF 失败");
        }
        if (pdf == null || pdf.length == 0) {
            return R.failed("生成 PDF 失败");
        }

        // 将 PDF 数据转换为 Base64 编码
        String base64Pdf = Base64.getEncoder().encodeToString(pdf);

        return R.ok(base64Pdf);
    }

    private List<String> checkData(List<ServiceExcel> list, List<ServiceExcel> failList) {
        LambdaQueryWrapper<StationServiceBaseInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(StationServiceBaseInfo::getName, list.stream().map(ServiceExcel::getStationName).collect(Collectors.toList()));
        List<StationServiceBaseInfo> stations = stationServiceBaseInfoMapper.selectList(queryWrapper);
        List<String> existStation = stations.stream().map(StationServiceBaseInfo::getName).toList();
        List<String> unAuthorized = stations.stream().filter(a -> a.getAuthorizationStart() == null).map(StationServiceBaseInfo::getName).toList();
        List<String> ids = stations.stream().filter(a -> a.getAuthorizationStart() != null).map(StationServiceBaseInfo::getId).toList();

        for (ServiceExcel serviceExcel : list) {
            if (!existStation.contains(serviceExcel.getStationName())) {
                serviceExcel.setErrResult("服务站不存在");
                failList.add(serviceExcel);
            }else if (unAuthorized.contains(serviceExcel.getStationName())) {
                serviceExcel.setErrResult("授权信息不存在，请单条添加 ");
                failList.add(serviceExcel);
            }
        }
        return ids;
    }

    List<String> readExcelHeaders(MultipartFile file) throws IOException {
        List<String> headers = new ArrayList<>();
        Workbook workbook = ExcelUtils.getWorkBook(file);
//        Workbook workbook = new XSSFWorkbook(file.getInputStream());
        Sheet sheet = workbook.getSheetAt(0);
        Row headerRow = sheet.getRow(0);

        for (Cell cell : headerRow) {
            CellType cellType = cell.getCellType();
            if (cellType== CellType.STRING){
                headers.add(cell.getStringCellValue());
            }
        }
        workbook.close();
        return headers;
    }

    public String exportErrList(List<ServiceExcel> fail)throws IOException{
        String basePath = "/data/cglx/errFiles/";
        String fileName = UUID.randomUUID().toString()+".xlsx";
        ExcelUtils.export(new File(basePath+fileName),fail, ServiceExcel.class);
        return fileName;
    }
}
